package com.hp.bon.sgw.service;

import com.hp.bon.sgw.bean.AuthResult;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.domain.HomeQueryTagConfig;
import com.hp.bon.sgw.domain.TPArea;
import com.hp.bon.sgw.domain.TPHcode;
import com.hp.bon.sgw.util.Comutils;
import com.hp.bon.sgw.util.dcc.DccAvpHelper;
import com.hp.ocs.diameter.DiameterUtils;
import com.hp.ocs.diameter.message.AVP;
import com.hp.ocs.diameter.message.AVP_Grouped;
import com.hp.ocs.diameter.message.AVP_OctetString;
import com.hp.ocs.diameter.message.AVP_UTF8String;
import com.hp.ocs.diameter.message.AVP_Unsigned32;
import com.hp.ocs.diameter.message.DictionaryManager;
import com.hp.ocs.diameter.message.Message;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.dom4j.Element;
import org.dom4j.Node;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Transactional;

public class QueryHomeAreaByAccNbrService
{
  private ConfigService confSrv;
  private SessionFactory sessionFactory;
  private SGWDataStoreService dataStoreService;
  private static final Logger logger = LoggerFactory.getLogger(QueryHomeAreaByAccNbrService.class);

  @Transactional
  public int queryHomeArea(CallParam callParam)
  {
    String serviceContextId = callParam.getServiceContextId();
    if (serviceContextId.contains("@000")) {
      logger.info("need do home area query ... ");
      AuthResult result = null;
      if ((callParam.getRequest() instanceof Message))
        result = doQuery(callParam.getCapability(), (Message)callParam.getRequest());
      else if ((callParam.getRequest() instanceof XmlMessage))
      {
        result = doQuery(callParam.getCapability(), ((XmlMessage)callParam.getRequest()).getDataElement());
      }
      if (result != null) {
        if (result.getCode() == 0) {
          logger.info("do home area query success: change serviceContextId's domain from 000 to " + result.getHint());
          callParam.setServiceContextId(serviceContextId.replaceAll("@000", "@" + result.getHint()));
        } else {
          return result.getCode();
        }
      }
      else return 11304;

    }

    return 0;
  }

  private AuthResult doQuery(String capability, Element reqData)
  {
    String errMsg = "";
    HomeQueryTagConfig conf = (HomeQueryTagConfig)this.confSrv.getHomeQueryTagConfigMap().get(capability);
    if (conf == null) {
      errMsg = capability + " not config home query tag";
      logger.warn(errMsg);
      return new AuthResult(11301, errMsg);
    }
    Node acc = reqData.selectSingleNode(conf.getAccXMLTag());
    Node accAttr = reqData.selectSingleNode(conf.getAccAttrXMLTag());

    String accValue = "";
    if (acc != null) {
      accValue = acc.getText().trim();
    } else {
      errMsg = "Cannot find " + conf.getAccXMLTag() + " tag";
      logger.warn(errMsg);
      return new AuthResult(11302, errMsg);
    }
    int attrVal = -1;
    if ((accAttr != null) && (!"".equals(accAttr.getText().trim()))) {
      attrVal = Integer.valueOf(accAttr.getText().trim()).intValue();
    }
    return doQueryAreaHome(conf, accValue, attrVal, conf.getAccType());
  }

  private AuthResult doQuery(String capability, Message msg)
  {
    String errMsg = "";
    DictionaryManager _dic = DictionaryManager.getInstance();
    HomeQueryTagConfig conf = (HomeQueryTagConfig)this.confSrv.getHomeQueryTagConfigMap().get(capability);
    if (conf == null) {
      errMsg = capability + " not config HomeQuery tag";
      logger.warn(errMsg);
      return new AuthResult(11301, errMsg);
    }
    AVP accAvp = msg.findAvpByNamePath(conf.getAccAVPName());
    AVP attrAvp = msg.findAvpByNamePath(conf.getAccAttrAVPName());
    String accValue = "";
    int attrVal = -1;
    if (accAvp == null) {
      errMsg = "Cannot find " + conf.getAccAVPName() + " AVP";
      logger.warn(errMsg);
      return new AuthResult(11302, errMsg);
    }
    try {
      accValue = _dic.getAvpValueAsString(accAvp);
      if (attrAvp != null) {
        attrVal = _dic.getAvpValueAsInt(attrAvp);
      }
      return doQueryAreaHome(conf, accValue, attrVal, conf.getAccType());
    } catch (Exception e) {
      errMsg = "process " + conf.getAccAVPName() + " AVP error";
      logger.warn(errMsg);
    }return new AuthResult(11302, errMsg);
  }

  private AuthResult doQueryAreaHome(HomeQueryTagConfig conf, String accValue, int attrVal, String accType)
  {
    TPArea area = null;
    try
    {
      if ("BON".equals(conf.getAccType()))
      {
        if ((attrVal == -1) || (attrVal == 2)) {
          area = queryByHcode(accValue, attrVal);
        } else if ((attrVal == 0) || (attrVal == 1) || (attrVal == 3)) {
          area = queryByArea(accValue, attrVal);
        } else {
          String errMsg = "Destination Account Attr value is not valid";
          logger.warn(errMsg);
          return new AuthResult(11303, errMsg);
        }
      } else if ("E164".equals(conf.getAccType()))
      {
        if ((attrVal != 0) && (attrVal != -1)) {
          return new AuthResult(11303, "not E.164 type");
        }

        String _accValue = accValue;
        if (accValue.startsWith("86")) {
          _accValue = accValue.substring(2, accValue.length());
        }
        area = queryByHcode(_accValue, 2);
        if (area == null) {
          area = queryByArea(_accValue, 0);
        }
      }
      if (area != null) {
        String prov = Comutils.getFamNum(area.getProvId(), 3);
        return new AuthResult(0, prov);
      }
      String errMsg = "Cannot find this account's home area: " + accValue;
      logger.warn(errMsg);
      return new AuthResult(11304, errMsg);
    }
    catch (Exception e) {
      String errMsg = " do home area query exception : " + e;
      logger.warn(errMsg);
      return new AuthResult(11304, errMsg);
    }
  }

  private TPArea queryByHcode(String accValue, int attrVal)
  {
    int nbrInt = 0;
    if (accValue.length() < 7) {
      logger.warn("Destination Account is vaild :" + accValue);
      return null;
    }
    nbrInt = Integer.parseInt(accValue.substring(0, 7));
    TPArea area = findByAccNbrExptTspId99(nbrInt);
    if (area == null) {
      logger.warn("cannot find tp_hcode from local");
      area = queryHomeByHss(accValue, attrVal);
      if (area != null) {
        saveHcode(area, nbrInt);
      }
    }
    return area;
  }

  private TPArea queryByArea(String accValue, int attrVal)
  {
		String acc = Integer.valueOf(accValue) + "";
    int area0 = Integer.parseInt(acc.substring(0, 2));
    int area1 = Integer.parseInt(acc.substring(0, 3));
    int area2 = Integer.parseInt(acc.substring(0, 4));
    TPArea area = findByAreaCode(area0, area1, area2);
    if (area == null) {
      logger.warn("cannot find tp_area from local");
      area = queryHomeByHss(accValue, attrVal);
      if (area != null) {
        saveArea(area);
      }
    }
    return area;
  }

  private void saveArea(TPArea area)
  {
    String sql = "insert into  TP_AREA (AREA_CODE,PROV_ID,AREA_NAME,VALID_TIME,ELAPSE_TIME) values (" + area.getAreaCode() + "," + area.getProvId() + ",''" + ",sysdate,null)";
    this.dataStoreService.addHSQL(sql, false);
    logger.info("save or update tp_area :" + area.toString());
  }

  private void saveHcode(TPArea area, int nbrInt)
  {
    TPHcode tpc = new TPHcode();
    tpc.setHcodeId(nbrInt);
    tpc.setAreaCode(area.getAreaCode());
    tpc.setTspId(-1);
    tpc.setValidTime(new Date());
    tpc.setElapseTime(null);

    String sql = "insert into TP_HCODE (HCODE_ID,TSP_ID,AREA_CODE,VALID_TIME,ELAPSE_TIME) values (" + nbrInt + "," + -1 + "," + area.getAreaCode() + ",sysdate,null)";
    this.dataStoreService.addHSQL(sql, false);
    logger.info("save or update tp_hcode :" + tpc.toString());
  }

  private TPArea findByAreaCode(int area1, int area2, int area3)
  {
    String hql = "select a from TPArea a where (a.areaCode=:area1 or a.areaCode=:area2 or a.areaCode=:area3) and a.validTime<= :now and (a.elapseTime >= :now or a.elapseTime is null)";
    Query query = this.sessionFactory.getCurrentSession().createQuery(hql);
    query.setParameter("area1", Integer.valueOf(area1));
    query.setParameter("area2", Integer.valueOf(area2));
    query.setParameter("area3", Integer.valueOf(area3));
    query.setParameter("now", new Date());
    if (!query.list().isEmpty()) {
      return (TPArea)query.list().get(0);
    }
    return null;
  }

  private TPArea findByAccNbrExptTspId99(int nbr)
  {
    String hql = "select a from TPArea a,TPHcode h where h.hcodeId=:nbr and a.areaCode=h.areaCode and h.tspId<>99 and h.validTime<= :now and (h.elapseTime >= :now or h.elapseTime is null)";
    Query query = this.sessionFactory.getCurrentSession().createQuery(hql);
    query.setParameter("nbr", Integer.valueOf(nbr));
    query.setParameter("now", new Date());
    if (!query.list().isEmpty()) {
      return (TPArea)query.list().get(0);
    }
    return null;
  }

  private TPArea queryHomeByHss(String accNbr, int destionationAttr)
  {
    String serviceContextId = "QueryHomeAreaByAccNbr.HSS@001.ctnbc-bon.net";
    Message message = new Message();

    String sessionId = DiameterUtils.makeSessionID(ConfigService.sgwHostId);
    DccAvpHelper.creatComMessageBeforCCR(message, sessionId, "QueryHomeAreaByAccNbr.HSS@001.ctnbc-bon.net");

    AVP oh = new AVP_OctetString(264, ConfigService.sgwHostId);
    oh.setMandatory(true);
    message.add(oh);

    AVP ohr = new AVP_OctetString(296, DccAvpHelper.getRealmPart(ConfigService.sgwHostId));
    ohr.setMandatory(true);
    message.add(ohr);

    AVP accNbrAVP = new AVP_OctetString(80125, 81000, accNbr);
    accNbrAVP.setMandatory(true);

    AVP destionationAttrAvp = new AVP_Unsigned32(5055, 81000, destionationAttr);
    destionationAttrAvp.setMandatory(true);

    AVP serviceInfomation = new AVP_Grouped(873, 10415, new AVP[] { accNbrAVP, destionationAttrAvp });
    serviceInfomation.setMandatory(true);

    message.add(serviceInfomation);

    Message cca = null;

    if (cca == null) {
      logger.warn("Do QueryHomeAreaByAccNbr.HSS failed,cannot receive CCA");
      return null;
    }
    try {
      AVP avpServiceInfomation = cca.find(873, 10415);
      AVP_Grouped serviceInfomationGrouped = new AVP_Grouped(avpServiceInfomation);

      AVP provinAVP = serviceInfomationGrouped.find(81105, 81000);
      AVP areaAVP = serviceInfomationGrouped.find(80615, 81000);

      if ((provinAVP != null) && (areaAVP != null)) {
        TPArea area = new TPArea();
        String province = new AVP_UTF8String(provinAVP).queryValue();
        int areacode = new AVP_Unsigned32(areaAVP).queryValue();
        area.setProvId(Integer.valueOf(province).intValue());
        area.setAreaCode(areacode);
        return area;
      }
    } catch (Exception e) {
      logger.warn("Do QueryHomeAreaByAccNbr.HSS failed,process CCA error:" + e);
    }
    return null;
  }

  @Autowired
  public void setConfSrv(ConfigService confSrv) {
    this.confSrv = confSrv;
  }
  @Autowired
  @Qualifier("sessionFactory")
  public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }

  @Autowired
  public void setDataStoreService(SGWDataStoreService dataStoreService) {
    this.dataStoreService = dataStoreService;
  }
}